逆向微信根据手机号码搜索联系人获取WXID

这段时间在忙着新版本SDK的开发,也在准备换工作的事情,虽然现在是“寒潮”,但是还是想走出舒适区,去感受一下春天的“寒冷”。也很久没有接触逆向方面的工作了,昨天一个大学同学问我,能不能通过一个人的手机号以及微信号找到那个人对应的wxid,其实我也不清楚,但是我们来尝试一下找一下。

1. wxid 是什么?

wxid是顾名思义就是微信的ID,也就是一个手机号码刚注册的时候,微信给予用户的一个初始化ID,这个和后面的微信号其实是分开的,经过我测试,如果设置了微信号之后,可能原始的wxid就会被覆盖。但是也有一些是不会被覆盖的,这个就不清楚微信是怎么处理的了。

2. 开始wxid的寻找之旅

2.1 分析

当我们通过手机号码查找一个人的时候,查找出来的界面是只会显示下面的信息,不会有wxid或者微信号这样的信息。

-w160

但是直觉告诉我,既然已经搜出来这个人,那么这个控制器里面肯定有一个包含这个人信息的对象,看一下那个对象里面的全部信息,可能会找到我想要的。所以我们的目标就是找到这个对象的控制器,然后看看里面有没有一个类似于联系人之类的对象,然后再看一下这个对象里面的属性。Let’s do it.

2.2 探究

在探究之前如果你不清楚LLDB+DebugServer的使用可以看这里

2.3 首先拿出我那台祖传的iPhone,连上数据线,然后SSH登陆上去:

  • iTerm打开一个窗口,将本地的2222端口映射到iPhone的22端口
1
2
3
Last login: Thu Mar 28 11:20:01 on ttys003
LemonsMcBookPro:~ lemon$ tcprelay.py -t 22:2222
Forwarding local port 2222 to remote port 22
  • iTerm打开另外一个窗口,通过ssh连接上去
1
2
LemonsMcBookPro:~ lemon$ ssh root@localhost -p 2222
lemons-iPhone5S:~ root#

2.4 登陆上去之后,看一下当前微信的进程ID,然后通过将debugserver挂到微信上面,然后接受来自LLDB的命令,达到动态调试微信的目的。

  • 先在手机运行微信,然后查找微信的进程ID,然后通过debugserver监听来自1234端口的消息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
LemonsMcBookPro:~ lemon$ ssh root@localhost -p 2222
lemons-iPhone5S:~ root# ps -e | grep /var
632 ?? 0:04.03 /usr/libexec/pkd -d/var/db/PlugInKit-Annotations
1902 ?? 0:11.14 /var/containers/Bundle/Application/2A191819-B0B1-41DB-813A-ED369475D3B9/NewsLite.app/NewsLite
1910 ?? 0:02.20 /var/containers/Bundle/Application/0841859F-B658-4A66-AB81-6B51C4A04D7B/Chain.app/Chain
1991 ?? 0:45.40 /var/containers/Bundle/Application/81DE6416-3F32-4C7E-BD0C-ABA1585E77E2/wework.app/wework
2612 ?? 2:16.97 /var/containers/Bundle/Application/5FE48592-3233-47A2-B933-C2F0CF277F14/WeChat.app/WeChat
2721 ?? 0:31.45 /var/containers/Bundle/Application/A7E1F925-9859-4C2C-9092-C10B719E604F/AndFetion.app/AndFetion
2996 ?? 0:04.00 /var/containers/Bundle/Application/06B248D3-61C9-48EC-98BF-A95BEFC782BA/WhatsApp.app/WhatsApp
3392 ttys000 0:00.01 grep /var
lemons-iPhone5S:~ root# debugserver *:1234 -a 2612
debugserver-@(#)PROGRAM:debugserver PROJECT:debugserver-340.3.124
for arm64.
Attaching to process 2612...
Listening to port 1234 for a connection from *...
  • 此时debugserver正在监听1234端口的消息。此时我们还是通过将本地的1234端口映射到iPhone的1234端口,然后连接本地的1234端口。
    iTerm 打开一个新窗口
1
2
3
4
5
Last login: Thu Mar 28 11:20:24 on ttys003
LemonsMcBookPro:~ lemon$ tcprelay.py -t 1234:1234
Forwarding local port 1234 to remote port 1234
Incoming connection to 1234
Waiting for devices...

iTerm 打开一个新窗口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Last login: Thu Mar 28 11:31:52 on ttys005
LemonsMcBookPro:~ lemon$ lldb
(lldb) process connect connect://localhost:1234
Process 2612 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #0: 0x00000001819b0fd8 libsystem_kernel.dylib`mach_msg_trap + 8
libsystem_kernel.dylib`mach_msg_trap:
-> 0x1819b0fd8 <+8>: ret

libsystem_kernel.dylib`mach_msg_overwrite_trap:
0x1819b0fdc <+0>: mov x16, #-0x20
0x1819b0fe0 <+4>: svc #0x80
0x1819b0fe4 <+8>: ret
Target 0: (WeChat) stopped.
(lldb)
  • 此时微信的进程已经停止了,并且正在等待我们的命令。我们先输入c让进程继续。(这是LLDB的基础命令,c代表continue)

2.5 打开目标界面,查找目标对象

  • 上面我们已经将LLDB挂到微信上面了,接下来我们先搜索一个手机号,然后去到添加联系人界面。
    -w160

  • 我们搜索到联系人之后,我们需要找到当前界面的控制器。下面是关键命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
(lldb) process interrupt
(lldb) po [[[UIApplication sharedApplication]keyWindow]recursiveDescription]
<MMUILongPressImageView: 0x140982bf0; baseClass = UIImageView; frame = (3 3; 64 64); opaque = NO; layer = <CALayer: 0x14338b660>>
<UIImageView: 0x14337eb10; frame = (0 0; 70 70); hidden = YES; userInteractionEnabled = NO; layer = <CALayer: 0x14337ecc0>>
<MMCPLabel: 0x1433cf8f0; baseClass = UILabel; frame = (107 0; 88 27); text = '默默等待'; tag = 90220; gestureRecognizers = <NSArray: 0x143517010>; layer = <_UILabelLayer: 0x1402ac6c0>>
<_UILabelContentLayer: 0x143398ee0> (layer)
<UIView: 0x1433e1600; frame = (304 1.5; 0 24); layer = <CALayer: 0x1433e1520>>
(lldb) po [0x1433cf8f0 nextResponder]
<MMUIButton: 0x143581b10; baseClass = UIButton; frame = (0 0; 320 77); opaque = NO; tag = 90221; layer = <CALayer: 0x14354ff50>>

(lldb) po [0x143581b10 nextResponder]
<UIView: 0x140982a80; frame = (0 0; 320 105); layer = <CALayer: 0x143541d80>>

(lldb) po [0x140982a80 nextResponder]
<WCStoryTableView: 0x13f5f7a00; baseClass = UITableView; frame = (0 0; 320 568); autoresize = W+H; gestureRecognizers = <NSArray: 0x143510ee0>; layer = <CALayer: 0x1433f3570>; contentOffset: {0, -64}; contentSize: {320, 504}>

(lldb) po [0x13f5f7a00 nextResponder];
<MMUIButton: 0x1426b1960; baseClass = UIButton; frame = (0 0; 320 1136); opaque = NO; autoresize = W; gestureRecognizers = <NSArray: 0x14315c0c0>; layer = <CALayer: 0x14093f9a0>>

(lldb) po [0x1426b1960 nextResponder]
<UIView: 0x14354d550; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x13ef32f00>>

(lldb) po [0x1426b1960 nextResponder];
<UIView: 0x14354d550; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x13ef32f00>>

(lldb) po [0x14354d550 nextResponder];
<ContactInfoViewController: 0x13fc0c400>

process interrupt: 中断当前进程
[[[UIApplication sharedApplication]keyWindow]recursiveDescription] : 答应当前界面的信息,此时会显示所有的界面的元素。

此时我们可以根据名字“默默等待”来找到到对应的Label,然后通过这个label的找到对应ViewController。使用nextResponder一级一级的找到对应的ViewController。通过上面的信息我们可以看到当前的ViewController是ContactInfoViewController。

2.6 打印对象属性。

  • 打印ContactInfoViewController的所有成员变量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
(lldb) po [0x13fc0c400 _ivarDescription]
<ContactInfoViewController: 0x13fc0c400>:
in ContactInfoViewController:
m_uiVerify (unsigned int): 0
m_contact (CContact*): <CSearchedContact: 0x1433b0550>
m_chatContact (CContact*): nil
m_delegate (<contactInfoDelegate>*): nil
m_InfoDelegate (<ContactInfoViewControllerDelegate>*): nil
m_oContactInfoAssist (CBaseContactInfoAssist*): <WeixinContactInfoAssist: 0x14331a240>
m_contactVerifyLogic (CContactVerifyLogic*): nil
m_nsLocation (NSString*): nil
m_bPopToRootWhenDelete (BOOL): <00>
m_uiFromScene (unsigned int): 6
m_wcOperateMode (int): 0
m_popToViewControllerClassWhenDelete (Class): (null)
m_searchScene (int): 0
m_userData (id): nil
m_clickHelper (FavClickStreamHelper*): <FavClickStreamHelper: 0x14260e330>
m_CurrentWidth (double): 320
m_forwardLogic (ForwardMessageLogicController*): nil
_startTime (unsigned int): 1553744360
m_sendVerifylogicVC (SendVerifyMsgLogicController*): nil
_isFullScreenShowStory (BOOL): <00>
_hasStoryData (BOOL): <00>
_canShowStoryView (BOOL): <01>
_isShowingStoryPreview (BOOL): <00>
m_uiAddFriendStatScene (int): 0
_m_qrCodeAddFriendScene (int): 0
_searchFromIndex (unsigned int): 0
_favId (unsigned int): 0
_verifyUserInfoList (NSMutableDictionary*): nil
_searchId (NSString*): nil
_searchKeyword (NSString*): nil
........

这个时候我们可以看到在最上面有一个m_contact (CContact*): <CSearchedContact: 0x1433b0550>我猜测这个应该是我们搜索出来的微信号信息的对象,我们可以试着打印一下这个对象看看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
(lldb) po [0x1433b0550 _ivarDescription]
<CSearchedContact: 0x1433b0550>:
in CSearchedContact:
matchType (unsigned int): 2
searchString (NSString*): @"18575784615"<__NSCFString: 0x1435838e0>
in CContact:
m_uiChatRoomStatus (unsigned int): 0
m_nsChatRoomMemList (NSString*): nil
m_nsChatRoomAdminList (NSString*): nil
m_uiChatRoomAccessType (unsigned int): 0
m_uiChatRoomMaxCount (unsigned int): 0
m_uiChatRoomVersion (unsigned int): 0
m_ChatRoomDetail (ChatRoomDetail*): nil
m_nsChatRoomData (NSString*): nil
m_ChatRoomData (ChatRoomData*): nil
m_nsCountry (NSString*): @"CN"<NSTaggedPointerString: 0xa00000000004e432>
m_nsProvince (NSString*): @"Anhui"<NSTaggedPointerString: 0xa00006975686e415>
m_nsCity (NSString*): @"Fuyang"<NSTaggedPointerString: 0xa00676e617975466>
m_nsSignature (NSString*): @""<__NSCFConstantString: 0x10adaff28>
m_uiCertificationFlag (unsigned int): 0
m_nsCertificationInfo (NSString*): @""<__NSCFConstantString: 0x10adaff28>
m_nsOwner (NSString*): nil
m_nsFBNickName (NSString*): nil
m_nsFBID (NSString*): nil
m_uiNeedUpdate (unsigned int): 0
m_nsWCBGImgObjectID (NSString*): @"0"<NSTaggedPointerString: 0xa000000000000301>
m_iWCFlag (int): 0
m_pcWCBGImgID (NSString*): @""<__NSCFConstantString: 0x10adaff28>
m_nsExternalInfo (NSString*): @""<__NSCFConstantString: 0x10adaff28>
m_nsBrandSubscriptConfigUrl (NSString*): @""<__NSCFConstantString: 0x10adaff28>
m_uiBrandSubscriptionSettings (unsigned int): 0
m_subBrandInfo (SubscriptBrandInfo*): <SubscriptBrandInfo: 0x140936fe0>
m_nsBrandIconUrl (NSString*): @""<__NSCFConstantString: 0x10adaff28>
m_isExtInfoValid (BOOL): <01>
externalInfoJSONCache (NSDictionary*): nil
m_isShowRedDot (BOOL): <00>
m_nsMobileHash (NSString*): nil
m_nsMobileFullHash (NSString*): nil
m_nsLinkedInID (NSString*): nil
m_nsLinkedInName (NSString*): nil
m_nsLinkedInPublicUrl (NSString*): nil
m_uiDeleteFlag (unsigned int): 0
m_nsDescription (NSString*): nil
m_nsCardUrl (NSString*): nil
m_nsWorkID (NSString*): nil
m_nsLabelIDList (NSString*): nil
m_arrPhoneItem (NSArray*): <__NSArrayM: 0x1433d60a0>
m_lockForChatRoomData (NSRecursiveLock*): <NSRecursiveLock: 0x1406dada0>
_appBrandInfo (CAppBrandInfo*): nil
_m_bFromNewDB (BOOL): <01>
_m_hasNewBizMsg (BOOL): <00>
_m_uiLastUpdate (unsigned int): 0
_m_uiMetaFlag (unsigned int): 0
_m_uiDebugModeType (unsigned int): 0
_m_uiWxAppOpt (unsigned int): 0
_uiLastUpdateAppVersionInfoTime (unsigned int): 0
openIMAppid (NSString*): nil
openIMInfo (COpenIMInfo*): nil
_m_dicWeiDianInfo (NSDictionary*): nil
_m_nsWeiDianInfo (NSString*): nil
in CBaseContact:
m_nsUsrName (NSString*): @"v1_81f423f663f69d690124bb98c81aade8c47642dfb41842bb2bdfd85907d912df04b2f31b8f910255577a8cde8dc835d4@stranger"<__NSCFString: 0x1434c5630>
m_nsEncodeUserName (NSString*): nil
m_nsAliasName (NSString*): @"yao1054057455"<__NSCFString: 0x1432f4c60>
m_uiConType (unsigned int): 0
m_nsNickName (NSString*): @"默默等待"<__NSCFString: 0x1434b18c0>
m_nsFullPY (NSString*): @"wxid0rxnvbwkf1n512132"<__NSCFString: 0x14324d5f0>
m_nsShortPY (NSString*): @"mmdd"<__NSCFString: 0x143526120>
m_nsRemark (NSString*): nil
m_nsRemarkPYShort (NSString*): nil
m_nsRemarkPYFull (NSString*): nil
m_nsDisplayNamePY (NSString*): nil
m_uiSex (unsigned int): 2
m_uiType (unsigned int): 0
m_uiChatState (unsigned int): 0
m_dtUsrImg (NSData*): nil
m_nsImgStatus (NSString*): @"IMG_HAS"<__NSCFConstantString: 0x104085c48>
m_nsHDImgStatus (NSString*): nil
m_nsHeadImgUrl (NSString*): @"http://wx.qlogo.cn/mmhead/ver_1/x0enJ9WJoJ9lSdibxAk8ukuriaLjBphAFdnAfib9pLM6fZXJclib1uyenjSr3fUdVSBOAfdlAAyrs8JhSbbOatpRUTgeqO4ReTkNG9ahRpJJHms/132"<__NSCFString: 0x1426bc500>
m_nsHeadHDImgUrl (NSString*): @"http://wx.qlogo.cn/mmhead/ver_1/x0enJ9WJoJ9lSdibxAk8ukuriaLjBphAFdnAfib9pLM6fZXJclib1uyenjSr3fUdVSBOAfdlAAyrs8JhSbbOatpRUTgeqO4ReTkNG9ahRpJJHms/0"<__NSCFString: 0x14354d710>
m_nsHeadHDMd5 (NSString*): nil
m_nsDraft (NSString*): nil
m_uiDraftTime (unsigned int): 0
m_nsAtUserList (NSString*): nil
m_uiQQUin (unsigned int): 0
m_nsMobileIdentify (NSString*): nil
m_uiFriendScene (unsigned int): 15
m_uiImgKey (unsigned int): 0
m_uiExtKey (unsigned int): 0
m_uiImgKeyAtLastGet (unsigned int): 0
m_uiExtKeyAtLastGet (unsigned int): 0
m_hasDetectPlugin (BOOL): <01>
m_isPlugin (BOOL): <00>
m_hasDetectSelf (BOOL): <01>
m_isSelf (BOOL): <00>
m_nsAntispamTicket (NSString*): @"v2_2e4d1f9deb2d50cec867afc7481db2adc7bf821f79562b66d83225ae2338039a7abd1160dcc4a7122640fb42e0784801@stranger"<__NSCFString: 0x143539fe0>
_externalInfoJSONCache (NSDictionary*): nil
in NSObject:
isa (Class): CSearchedContact (isa, 0x81a104c3d59d)

结果没有让我们失望,到这里已经看到了m_nsFullPY这个属性包含的就是wxid。这次逆向很简单,都没有用到静态分析看汇编代码等方法,直接就能实现我们想做的。

最后有个小tips,一般通过wxid是加不了好友的,但是在wxid前面加几个汉字可以搜索到好友哦。

-------评论系统采用disqus,如果看不到需要翻墙-------------